﻿#include "effects/fixedGaussian.h"

#include "common.h"
#include "texture.h"
#include "shader.h"
#include "context.h"
#include "target.h"
#include <exception>

namespace anip
{
	FixedGaussianBlurEffect::FixedGaussianBlurEffect(int step) :
		_step(step), _iter(1), _shader(nullptr), _target(nullptr)
	{
		texParams = new TextureParameter[1];
		texOutputs = new TextureParameter[1];
	}

	void FixedGaussianBlurEffect::initialize()
	{
		assert(_step > 0);
		assert(texParams[0].w > 0 && texParams[0].h > 0);

		if (_target != nullptr)
		{
			_target->finalize();
			delete _target;
		}

		if (_shader != nullptr)
		{
			_shader->finalize();
			delete _shader;
		}

		int w = texParams[0].w, h = texParams[0].h;
		texOutputs[0].w = w;
		texOutputs[0].h = h;
		_target = new RenderBuffer(w, h);

		char buf[10];
		snprintf(buf, 10, "%d", _step);
		_name.assign("Gaussian blur, r=");
		_name.append(buf);

		float* nums = new float[_step + 1];
		float sum = 1;
		nums[0] = 1;
		for (int i = 1; i <= _step; i++)
		{
			nums[i] = expf(-i * (float)i / (_step * _step) * 2);
			sum += nums[i] * 2;
		}
		for (int i = 0; i <= _step; i++)
			nums[i] /= sum;

		string fragShader =
			"uniform sampler2D image;"
			"uniform vec2 step;"
			"in vec2 pos;"
			"void main()"
			"{"
			"gl_FragColor=texture2D(image,pos)*";
		char buf1[10], buf2[10];
		snprintf(buf1, 10, "%f", nums[0]);
		fragShader.append(buf1);
		for (int i = 1; i <= _step; i++)
		{
			snprintf(buf1, 10, "%.1f", (float)i);
			snprintf(buf2, 10, "%f", nums[i]);
			fragShader.append("+texture2D(image,pos+step*");
			fragShader.append(buf1);
			fragShader.append(")*");
			fragShader.append(buf2);

			fragShader.append("+texture2D(image,pos-step*");
			fragShader.append(buf1);
			fragShader.append(")*");
			fragShader.append(buf2);
		}
		fragShader.append(
			";"
			"}");
		delete[] nums;

		_shader = Shader::load(basicVert, fragShader);
	}

	void FixedGaussianBlurEffect::process(const Context& cxt)
	{
		const Texture2D& input = *texParams[0].value;

		float iw = 1.0f / input.width();
		float ih = 1.0f / input.height();

		_target->beginRender();

		_shader->use();
		_shader->set("image", input, 0);
		_shader->set("step", iw, 0.0f);
		cxt.fill();
		_shader->set("image", _target->getTexture(), 0);
		for (int i = 1; i < _iter; i++) cxt.fill();
		_shader->set("step", 0.0f, ih);
		for (int i = 0; i < _iter; i++) cxt.fill();

		texOutputs[0].value = &_target->getTexture();

		_target->endRender();
	}

	void FixedGaussianBlurEffect::finalize()
	{
		_target->finalize();
		_shader->finalize();
		delete _target;
		delete _shader;
		delete texParams;
		delete texOutputs;
	}
}
